package org.wikibrain.spatial.util; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.index.strtree.STRtree; import com.vividsolutions.jts.operation.distance.DistanceOp; import gnu.trove.map.TIntObjectMap; import gnu.trove.map.hash.TIntObjectHashMap; import java.io.Serializable; import java.util.ArrayList; import java.util.List; /** * @author Shilad Sen */ public class ContainmentIndex implements Serializable { /** * Default buffer width 1 in degrees. These are about 0.1 kms and 10 km. */ private static final double [] DEFAULT_BUFFER_WIDTHS = new double[] { 0.0, 0.001, 0.1 }; private final TIntObjectMap<Geometry> geometries = new TIntObjectHashMap<Geometry>(); private TIntObjectMap<Geometry> [] expanded; private STRtree[] indexes; private double [] bufferWidths; private boolean forceContains = false; public ContainmentIndex() { setBufferWidths(DEFAULT_BUFFER_WIDTHS); } public void insert(int id, Geometry geometry) { for (int i = 0; i < bufferWidths.length; i++) { Geometry exp = geometry.buffer(bufferWidths[i]); Envelope env = exp.getEnvelopeInternal(); synchronized (indexes[i]) { indexes[i].insert(env, id); expanded[i].put(id, exp); } synchronized (geometries) { geometries.put(id, geometry); } } } public List<Result> getContainer(Geometry query) { List<Result> results = new ArrayList<Result>(); for (int i = 0; i < bufferWidths.length; i++) { Envelope env = new Envelope(query.getEnvelopeInternal()); for (int id : (List<Integer>)indexes[i].query(env)) { Geometry g = expanded[i].get(id); if (g.contains(query)) { results.add(new Result(id, g)); } } if (!results.isEmpty()) break; } if (results.isEmpty() && forceContains) { double closestDistance = 0; int closestId = -1; for (int id : geometries.keys()) { Coordinate[] pair = DistanceOp.nearestPoints(query, geometries.get(id)); double d = pair[0].distance(pair[1]); if (closestId < 0 || d < closestDistance) { closestDistance = d; closestId = id; } } if (closestId >= 0) { results.add(new Result(closestId, geometries.get(closestId))); } } return results; } public void setBufferWidths(double [] bufferWidths) { this.bufferWidths = bufferWidths; this.indexes = new STRtree[this.bufferWidths.length]; this.expanded = new TIntObjectHashMap[this.bufferWidths.length]; for (int i = 0; i < bufferWidths.length; i++) { this.indexes[i] = new STRtree(); this.expanded[i] = new TIntObjectHashMap<Geometry>(); } } public static class Result { public final int id; public final Geometry geometry; public Result(int id, Geometry geometry) { this.id = id; this.geometry = geometry; } } public void setForceContains(boolean forceContains) { this.forceContains = forceContains; } }